/*
 *  Copyright 2004 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.persistence.hibernate.core;

import com.blandware.atleap.common.util.PartialCollection;
import com.blandware.atleap.common.util.QueryInfo;
import com.blandware.atleap.model.core.ContentResource;
import com.blandware.atleap.model.core.ResourceData;
import com.blandware.atleap.model.core.Role;
import com.blandware.atleap.model.core.statistics.Visit;
import com.blandware.atleap.persistence.core.ContentResourceDAO;
import com.blandware.atleap.persistence.core.StatisticsDAO;
import com.blandware.atleap.persistence.exception.DeleteException;

import java.util.*;

/**
 * <p>Hibernate implementation of ContentResourceDAO</p>
 * <p><a href="ContentResourceDAOHibernate.java.html"><i>View Source</i></a></p>
 *
 * @author Sergey Zubtsovskiy <a href="mailto:sergey.zubtsovskiy@blandware.com">&lt;sergey.zubtsovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.34 $ $Date: 2008/06/23 13:24:53 $
 */
public class ContentResourceDAOHibernate extends BaseDAOHibernate implements ContentResourceDAO {

    /**
     * DAO to work with statistics
     */
    protected StatisticsDAO statisticsDAO = null;

    /**
     * Sets DAO to work with statistics.
     *
     * @param statisticsDAO DAO to set
     */
    public void setStatisticsDAO(StatisticsDAO statisticsDAO) {
        this.statisticsDAO = statisticsDAO;
    }

	/**
	 * Creates new instance of ContentResourceDAOHibernate
	 */
	public ContentResourceDAOHibernate() {
	}

	// ~ CRUD Methods ================================================================

	/**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#createContentResource(com.blandware.atleap.model.core.ContentResource, com.blandware.atleap.model.core.ResourceData)
	 */
	public Long createContentResource(ContentResource contentResource, ResourceData resourceData) {
        contentResource.setLastUpdatedDatetime(new Date());
		contentResource.setUsageCounter(new Integer(0));
		getHibernateTemplate().save(resourceData);
		contentResource.setResourceData(resourceData);
		return (Long) getHibernateTemplate().save(contentResource);
	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#retrieveContentResource(java.lang.Long)
	 */
	public ContentResource retrieveContentResource(Long contentResourceId) {
		return (ContentResource) getHibernateTemplate().get(ContentResource.class, contentResourceId);
	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#updateContentResource(com.blandware.atleap.model.core.ContentResource, com.blandware.atleap.model.core.ResourceData)
	 */
	public void updateContentResource(ContentResource contentResource, ResourceData resourceData) {
        contentResource.setLastUpdatedDatetime(new Date());
		getHibernateTemplate().saveOrUpdate(resourceData);
		contentResource.setResourceData(resourceData);
		getHibernateTemplate().update(contentResource);
	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#deleteContentResource(com.blandware.atleap.model.core.ContentResource)
	 */
	public void deleteContentResource(ContentResource contentResource) throws DeleteException {
		getHibernateTemplate().delete(contentResource);

		// break relations between roles and deleted resource
		List roles = new ArrayList(contentResource.getRoles());
		for ( int i = 0; i < roles.size(); i++ ) {
			Role role = (Role) roles.get(i);
			contentResource.removeRole(role);
		}
	}

	// ~ Additional methods ================================================================

	/**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#listContentResources(com.blandware.atleap.common.util.QueryInfo)
	 */
	public PartialCollection listContentResources(QueryInfo queryInfo) {
		String whereClause = "";
		String orderByClause = "";
		if ( queryInfo != null ) {
			whereClause = queryInfo.getWhereClause();
			orderByClause = queryInfo.getOrderByClause();
			if ( whereClause == null ) {
				whereClause = "";
			}
			if ( orderByClause != null && orderByClause.length() != 0 ) {
				orderByClause = " order by " + orderByClause;
			} else {
				orderByClause = "";
			}
		}

		List list;
		Integer total = null;
		String hqlPart;
		hqlPart = "from ContentResource r left outer join r.roles as role";

		ArrayList args = new ArrayList();

        Map queryParameters = null;
        if (queryInfo != null) {
            queryParameters = queryInfo.getQueryParameters();
        }
        
        Object contentResourceType = null;
        if (queryInfo != null) {
            contentResourceType = queryParameters.get("contentResourceType");
        }
        if ( contentResourceType != null ) {
			hqlPart += " where (r.type = ?)";
			args.add(contentResourceType);
			if ( whereClause.length() > 0 ) {
				whereClause = " and " + whereClause;
			}
		} else if ( whereClause.length() > 0 ) {
			whereClause = " where " + whereClause;
		}

        boolean useCacheRegion = false;
        if (queryInfo != null) {
            useCacheRegion = Boolean.TRUE.equals(queryParameters.get("useCacheRegion"));
        }
        String cacheRegion = null;
        if (useCacheRegion) {
            cacheRegion = "query.listContentResources";
        }

        hqlPart += whereClause;

		if ( queryInfo != null && (queryInfo.getLimit() != null || queryInfo.getOffset() != null) ) {
			String hqlForTotal = "select count(distinct r.id) " + hqlPart;
			total = findUniqueIntegerResult(hqlForTotal, args.toArray());
			if ( total == null ) {
				total = new Integer(0);
			}

		}

        // If we don't have any info about the total number of results yet or
        // we know that there's something that will be found, then fetch data
		if ( total == null || total.intValue() > 0 ) {
			String hql = "select distinct r " + hqlPart + orderByClause;
			list = executeFind(hql, queryInfo, args.toArray(), useCacheRegion,
                               cacheRegion);
			if ( total == null ) {
				total = new Integer(list.size());
			}
		} else {
			list = new ArrayList();
		}
		return new PartialCollection(list, total);

	}

	/**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#getLastContentResources(com.blandware.atleap.common.util.QueryInfo)
	 */
	public List getLastContentResources(QueryInfo queryInfo) {

		String orderByClause = " order by r.lastUpdatedDatetime desc ";

		List list = new ArrayList();

		List roles = null;
		Integer limit = queryInfo.getLimit();
        if ( queryInfo.getQueryParameters() != null ) {
            roles = (List) queryInfo.getQueryParameters().get("roles");
        }
        if (roles == null) {
            roles = new ArrayList(0);
        }

        String hqlPart;
        hqlPart = "from ContentResource r ";

		String hql;
        hql = "select r " + hqlPart + orderByClause;

		Integer offset = new Integer(0);
		// Iterate until requested number of elements will be acquired.
        // This tricky loop is needed because we cannot predict how much of
        // resources we'll need to fetch (as some of them don't match to the
        // given roles).
		do {
			List tmp = executeFind(hql, queryInfo, null, true, "query.getLastContentResources");

			// if there are no more elements, break iteration
			if ( tmp.isEmpty() ) {
				break;
			}

            // filter by roles
            for ( Iterator i = tmp.iterator(); i.hasNext(); ) {
                ContentResource contentResource = (ContentResource) i.next();
                if (!contentResource.getRoles().isEmpty()) {
                    Set rolesSet = new HashSet(contentResource.getRoles());
                    rolesSet.retainAll(roles);
                    if ( rolesSet.isEmpty() ) {
                        i.remove();
                    }
                }
            }

            list.addAll(tmp);

			offset = new Integer(offset.intValue() + limit.intValue());
			queryInfo.setOffset(offset);
		} while ( list.size() < limit.intValue() );

		if ( list.size() > limit.intValue() ) {
			list = list.subList(0, limit.intValue());
		}

		return list;
	}

    /**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#hasDuplicates(com.blandware.atleap.model.core.ContentResource)
	 */
	public boolean hasDuplicates(ContentResource contentResource) {

		ArrayList args = new ArrayList();
		args.add(contentResource.getUri());

		String hql = "select count(r.id) from ContentResource r where r.uri = ?";
		if ( contentResource.getId() != null ) {
			hql += " and r.id != ?";
			args.add(contentResource.getId());
		}

		int count = (findUniqueIntegerResult(hql, args.toArray())).intValue();

		return count > 0;
	}

    /**
     * @see com.blandware.atleap.persistence.core.ContentResourceDAO#removeAssociations(com.blandware.atleap.model.core.ContentResource)
     */
    public void removeAssociations(ContentResource contentResource) {
		// break relations between visits and deleted resource
		List visits = statisticsDAO.listContentResourceVisits(contentResource.getId());
		for ( int i = 0; i < visits.size(); i++ ) {
			Visit visit = (Visit) visits.get(i);
			visit.setContentResource(null);
            statisticsDAO.updateVisit(visit);
		}
    }

    /**
	 * @see com.blandware.atleap.persistence.core.ContentResourceDAO#findContentResourceByUri(String)
	 */
	public ContentResource findContentResourceByUri(String uri) {
		return (ContentResource) findUniqueResult("from ContentResource r where r.uri = ?", new Object[]{uri});
	}
}
